widget: tighten the conditions at which a press event is emulated
authorCarlos Garnacho <carlosg@gnome.org>
Wed, 7 May 2014 17:08:50 +0000 (19:08 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Fri, 23 May 2014 17:54:30 +0000 (19:54 +0200)
Make it really sure that the event is only emitted after every gesture
that consumed the button press is done with the sequence.
The event must only be emulated if a gesture in the capture phase happened
to consume the event, be cancelled, and

gtk/gtkgesture.c
gtk/gtkwidget.c

index 420ad9ffa32676dbdb964170969345407591ada1..e738e61c1ca5bc25176ba375a36e1023a755f0f0 100644 (file)
@@ -510,7 +510,10 @@ gtk_gesture_handle_event (GtkEventController *controller,
           PointData *data;
 
           data = g_hash_table_lookup (priv->points, sequence);
-          data->press_handled = TRUE;
+
+          /* If the sequence was claimed early, the press event will be consumed */
+          if (gtk_gesture_get_sequence_state (gesture, sequence) == GTK_EVENT_SEQUENCE_CLAIMED)
+            data->press_handled = TRUE;
         }
 
       break;
@@ -820,7 +823,7 @@ gtk_gesture_set_sequence_state (GtkGesture            *gesture,
     return FALSE;
 
   if (data->state == state)
-    return TRUE;
+    return FALSE;
 
   /* denied sequences remain denied */
   if (data->state == GTK_EVENT_SEQUENCE_DENIED)
index 87ac87396da8083f5080258ef7ea6b49ba6721f2..18eed607168527e87852645e5a6c10a185b18e10 100644 (file)
@@ -4181,31 +4181,65 @@ _gtk_widget_get_emulating_sequence (GtkWidget         *widget,
 }
 
 static gboolean
+gtk_widget_needs_press_emulation (GtkWidget        *widget,
+                                  GdkEventSequence *sequence)
+{
+  GtkWidgetPrivate *priv = widget->priv;
+  gboolean sequence_press_handled = FALSE;
+  GList *l;
+
+  /* Check whether there is any remaining gesture in
+   * the capture phase that handled the press event
+   */
+  for (l = priv->event_controllers; l; l = l->next)
+    {
+      EventControllerData *data;
+      GtkGesture *gesture;
+
+      data = l->data;
+
+      if (data->phase != GTK_PHASE_CAPTURE)
+        continue;
+      if (!GTK_IS_GESTURE (data->controller))
+        continue;
+
+      gesture = GTK_GESTURE (data->controller);
+      sequence_press_handled |=
+        (gtk_gesture_handles_sequence (gesture, sequence) &&
+         _gtk_gesture_handled_sequence_press (gesture, sequence));
+    }
+
+  return !sequence_press_handled;
+}
+
+static gint
 _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
                                          GdkEventSequence      *sequence,
                                          GtkEventSequenceState  state,
-                                         GList                 *group)
+                                         GtkGesture            *emitter)
 {
+  gboolean emulates_pointer, sequence_handled = FALSE;
   GtkWidgetPrivate *priv = widget->priv;
   const GdkEvent *mimic_event;
-  gboolean send_event = FALSE;
-  gboolean emulates_pointer;
+  GList *group = NULL, *l;
   GdkEventSequence *seq;
-  gboolean handled = FALSE;
-  GList *l;
+  gint n_handled = 0;
 
   if (!priv->event_controllers && state != GTK_EVENT_SEQUENCE_CLAIMED)
     return TRUE;
 
+  if (emitter)
+    group = gtk_gesture_get_group (emitter);
+
   emulates_pointer = _gtk_widget_get_emulating_sequence (widget, sequence, &seq);
   mimic_event = _gtk_widget_get_last_event (widget, seq);
 
   for (l = priv->event_controllers; l; l = l->next)
     {
       GtkEventSequenceState gesture_state;
-      gboolean sequence_handled, retval;
       EventControllerData *data;
       GtkGesture *gesture;
+      gboolean retval;
 
       seq = sequence;
       data = l->data;
@@ -4216,6 +4250,14 @@ _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
 
       gesture = GTK_GESTURE (data->controller);
 
+      if (gesture == emitter)
+        {
+          sequence_handled |=
+            _gtk_gesture_handled_sequence_press (gesture, sequence);
+          n_handled++;
+          continue;
+        }
+
       if (seq && emulates_pointer &&
           !gtk_gesture_handles_sequence (gesture, seq))
         seq = NULL;
@@ -4233,29 +4275,30 @@ _gtk_widget_set_sequence_state_internal (GtkWidget             *widget,
         }
 
       g_signal_handler_block (data->controller, data->sequence_state_changed_id);
-
-      sequence_handled =
-        _gtk_gesture_handled_sequence_press (gesture, seq);
       retval = gtk_gesture_set_sequence_state (gesture, seq, gesture_state);
-      handled |= retval;
-
       g_signal_handler_unblock (data->controller, data->sequence_state_changed_id);
 
-      /* If the sequence goes denied, check whether this is a controller attached
-       * to the capture phase, that additionally handled the button/touch press (ie.
-       * it was consumed), the corresponding press will be emulated for widgets
-       * beneath, so the widgets beneath get a coherent stream of events from now on.
-       */
-      if (retval && sequence_handled &&
-          data->phase == GTK_PHASE_CAPTURE &&
-          state == GTK_EVENT_SEQUENCE_DENIED)
-        send_event = TRUE;
+      if (retval || gesture == emitter)
+        {
+          sequence_handled |=
+            _gtk_gesture_handled_sequence_press (gesture, seq);
+          n_handled++;
+        }
     }
 
-  if (send_event && mimic_event)
+  /* If the sequence goes denied, check whether this is a controller attached
+   * to the capture phase, that additionally handled the button/touch press (ie.
+   * it was consumed), the corresponding press will be emulated for widgets
+   * beneath, so the widgets beneath get a coherent stream of events from now on.
+   */
+  if (n_handled > 0 && sequence_handled &&
+      state == GTK_EVENT_SEQUENCE_DENIED &&
+      gtk_widget_needs_press_emulation (widget, sequence))
     _gtk_widget_emulate_press (widget, mimic_event);
 
-  return handled;
+  g_list_free (group);
+
+  return n_handled;
 }
 
 static gboolean
@@ -16683,12 +16726,9 @@ event_controller_sequence_state_changed (GtkGesture            *gesture,
   GtkWidget *event_widget;
   gboolean cancel = TRUE;
   const GdkEvent *event;
-  GList *group;
 
-  group = gtk_gesture_get_group (gesture);
   handled = _gtk_widget_set_sequence_state_internal (widget, sequence,
-                                                     state, group);
-  g_list_free (group);
+                                                     state, gesture);
 
   if (!handled || state != GTK_EVENT_SEQUENCE_CLAIMED)
     return;